1
|
|
|
var eventQueue = []; |
2
|
|
|
var svg; |
3
|
|
|
var element; |
4
|
|
|
var drawingArea; |
5
|
|
|
var width; |
6
|
|
|
var height; |
7
|
|
|
var volume = 0.6; |
8
|
|
|
var ULTIMATE_DREAM_KILLER = false; // https://github.com/debugger22/github-audio/pull/19 |
9
|
|
|
var orgRepoFilterNames = []; |
10
|
|
|
var header = $('header'); |
11
|
|
|
|
12
|
|
|
var scale_factor = 6, |
13
|
|
|
note_overlap = 2, |
14
|
|
|
note_timeout = 300, |
15
|
|
|
current_notes = 0, |
16
|
|
|
max_life = 20000; |
17
|
|
|
|
18
|
|
|
var svg_background_color_online = 'black', |
19
|
|
|
svg_background_color_offline = 'black', |
20
|
|
|
svg_text_color = '#FFFFFF', |
21
|
|
|
edit_color = '#FFFFFF', |
22
|
|
|
total_sounds = 51; |
23
|
|
|
|
24
|
|
|
var celesta = [], |
25
|
|
|
clav = [], |
26
|
|
|
swells = [], |
27
|
|
|
all_loaded = false; |
28
|
|
|
|
29
|
|
|
var socket = new WebSocket("ws://localhost:1337/ws"); |
30
|
|
|
|
31
|
|
|
socket.addEventListener("message", function (data) { |
32
|
|
|
var json = JSON.parse(data.data); |
33
|
|
|
|
34
|
|
|
if (json.hasOwnProperty('connectedUsers')) { |
35
|
|
|
$('.online-users-count').html(json.connectedUsers); |
36
|
|
|
|
37
|
|
|
return; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
json.forEach(function (event) { |
41
|
|
|
if (!isEventInQueue(event)) { |
42
|
|
|
// Filter out events only specified by the user |
43
|
|
|
if (orgRepoFilterNames != []) { |
44
|
|
|
// Don't consider pushes to github.io repos when org filter is on |
45
|
|
|
if (new RegExp(orgRepoFilterNames.join("|")).test(event.repoName) && event.repoName.indexOf('github.io') == -1) { |
46
|
|
|
eventQueue.push(event); |
47
|
|
|
} |
48
|
|
|
} else { |
49
|
|
|
eventQueue.push(event); |
50
|
|
|
} |
51
|
|
|
} |
52
|
|
|
}); |
53
|
|
|
|
54
|
|
|
// Don't let the eventQueue grow more than 1000 |
55
|
|
|
if (eventQueue.length > 1000) eventQueue = eventQueue.slice(0, 1000); |
|
|
|
|
56
|
|
|
}); |
57
|
|
|
|
58
|
|
|
socket.onopen = function(e){ |
|
|
|
|
59
|
|
|
$('svg').css('background-color', svg_background_color_online); |
60
|
|
|
header.css('background-color', svg_background_color_online); |
61
|
|
|
$('.offline-text').css('visibility', 'hidden'); |
62
|
|
|
$('.events-remaining-text').css('visibility', 'visible'); |
63
|
|
|
$('.events-remaining-value').css('visibility', 'visible'); |
64
|
|
|
$('.online-users-div').css('visibility', 'visible'); |
65
|
|
|
}; |
66
|
|
|
|
67
|
|
|
socket.onclose = function(e){ |
|
|
|
|
68
|
|
|
$('svg').css('background-color', svg_background_color_offline); |
69
|
|
|
header.css('background-color', svg_background_color_offline); |
70
|
|
|
$('.offline-text').css('visibility', 'visible'); |
71
|
|
|
$('.events-remaining-text').css('visibility', 'visible'); |
72
|
|
|
$('.events-remaining-value').css('visibility', 'visible'); |
73
|
|
|
}; |
74
|
|
|
|
75
|
|
|
socket.onerror = function(e){ |
|
|
|
|
76
|
|
|
$('svg').css('background-color', svg_background_color_offline); |
77
|
|
|
header.css('background-color', svg_background_color_offline); |
78
|
|
|
$('.offline-text').css('visibility', 'visible'); |
79
|
|
|
$('.events-remaining-text').css('visibility', 'visible'); |
80
|
|
|
$('.events-remaining-value').css('visibility', 'visible'); |
81
|
|
|
}; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* This function checks whether an event is already in the queue |
85
|
|
|
*/ |
86
|
|
|
function isEventInQueue(event){ |
87
|
|
|
for(var i=0; i<eventQueue.length; i++){ |
88
|
|
|
if(eventQueue[i].id == event.id) |
89
|
|
|
return true; |
|
|
|
|
90
|
|
|
} |
91
|
|
|
return false; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* This function adds a filter for events that we don't want to hear. |
96
|
|
|
* |
97
|
|
|
* To extend this function, simply add return true for events that should be filtered. |
98
|
|
|
*/ |
99
|
|
|
function shouldEventBeIgnored(event){ |
100
|
|
|
// This adds an easter egg to only play closed PRs |
101
|
|
|
if (!!ULTIMATE_DREAM_KILLER) |
102
|
|
|
return (event.type !== "PullRequestEvent" || event.action !== "closed"); |
|
|
|
|
103
|
|
|
|
104
|
|
|
return false; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
$(function(){ |
108
|
|
|
element = document.documentElement; |
109
|
|
|
drawingArea = document.getElementsByTagName('#area')[0]; |
110
|
|
|
width = window.innerWidth || element.clientWidth || drawingArea.clientWidth; |
111
|
|
|
height = (window.innerHeight - header.height()) || (element.clientHeight - header.height()) || (drawingArea.clientHeight - header.height()); |
112
|
|
|
$('svg').css('background-color', svg_background_color_online); |
113
|
|
|
header.css('background-color', svg_background_color_online); |
114
|
|
|
$('svg text').css('color', svg_text_color); |
115
|
|
|
$('#volumeSlider').slider({ |
116
|
|
|
'max': 100, |
117
|
|
|
'min': 0, |
118
|
|
|
'value': volume*100, |
119
|
|
|
'slide' : function(event, ui){ |
120
|
|
|
volume = ui.value/100.0; |
121
|
|
|
Howler.volume(volume); |
|
|
|
|
122
|
|
|
}, |
123
|
|
|
'change' : function(event, ui){ |
124
|
|
|
volume = ui.value/100.0; |
125
|
|
|
Howler.volume(volume); |
|
|
|
|
126
|
|
|
} |
127
|
|
|
}); |
128
|
|
|
|
129
|
|
|
// Main drawing area |
130
|
|
|
svg = d3.select("#area").append("svg"); |
|
|
|
|
131
|
|
|
svg.attr({width: width, height: height}); |
132
|
|
|
svg.style('background-color', svg_background_color_online); |
133
|
|
|
|
134
|
|
|
// For window resizes |
135
|
|
|
var update_window = function() { |
136
|
|
|
width = window.innerWidth || element.clientWidth || drawingArea.clientWidth; |
137
|
|
|
height = (window.innerHeight - header.height()) || (element.clientHeight - header.height()) || (drawingArea.clientHeight - header.height()); |
138
|
|
|
svg.attr("width", width).attr("height", height); |
139
|
|
|
}; |
140
|
|
|
window.onresize = update_window; |
141
|
|
|
update_window(); |
142
|
|
|
|
143
|
|
|
var loaded_sounds = 0; |
144
|
|
|
var sound_load = function(r) { |
|
|
|
|
145
|
|
|
loaded_sounds += 1; |
146
|
|
|
if (loaded_sounds == total_sounds) { |
147
|
|
|
all_loaded = true; |
148
|
|
|
setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000)); |
149
|
|
|
// Starting the second exchange makes music a bad experience |
150
|
|
|
} |
151
|
|
|
}; |
152
|
|
|
|
153
|
|
|
// Load sounds |
154
|
|
|
for (var i = 1; i <= 24; i++) { |
155
|
|
|
if (i > 9) { |
156
|
|
|
fn = 'c0' + i; |
|
|
|
|
157
|
|
|
} else { |
158
|
|
|
fn = 'c00' + i; |
159
|
|
|
} |
160
|
|
|
celesta.push(new Howl({ |
|
|
|
|
161
|
|
|
src : [ |
162
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.ogg', |
163
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.mp3' |
164
|
|
|
], |
165
|
|
|
volume : 0.7, |
166
|
|
|
onload : sound_load(), |
167
|
|
|
buffer: true |
168
|
|
|
})); |
169
|
|
|
clav.push(new Howl({ |
170
|
|
|
src : [ |
171
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.ogg', |
172
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.mp3' |
173
|
|
|
], |
174
|
|
|
volume : 0.4, |
175
|
|
|
onload : sound_load(), |
176
|
|
|
buffer: true |
177
|
|
|
})) |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
for (var i = 1; i <= 3; i++) { |
|
|
|
|
181
|
|
|
swells.push(new Howl({ |
182
|
|
|
src : [ |
183
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.ogg', |
184
|
|
|
'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.mp3' |
185
|
|
|
], |
186
|
|
|
volume : 1, |
187
|
|
|
onload : sound_load(), |
188
|
|
|
buffer: true |
189
|
|
|
})); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
Howler.volume(volume); |
|
|
|
|
193
|
|
|
|
194
|
|
|
// Make header and footer visible |
195
|
|
|
$('body').css('visibility', 'visible'); |
196
|
|
|
|
197
|
|
|
$('#org-repo-filter-name').on('input', function() { |
198
|
|
|
orgRepoFilterNames = $('#org-repo-filter-name').val().split(' '); |
199
|
|
|
eventQueue = []; |
200
|
|
|
}); |
201
|
|
|
|
202
|
|
|
}); |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Randomly selects a swell sound and plays it |
206
|
|
|
*/ |
207
|
|
|
function playRandomSwell() { |
208
|
|
|
var index = Math.round(Math.random() * (swells.length - 1)); |
209
|
|
|
swells[index].play(); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Plays a sound(celesta and clav) based on passed parameters |
214
|
|
|
*/ |
215
|
|
|
function playSound(size, type) { |
216
|
|
|
var max_pitch = 100.0; |
217
|
|
|
var log_used = 1.0715307808111486871978099; |
218
|
|
|
var pitch = 100 - Math.min(max_pitch, Math.log(size + log_used) / Math.log(log_used)); |
219
|
|
|
var index = Math.floor(pitch / 100.0 * Object.keys(celesta).length); |
220
|
|
|
|
221
|
|
|
index += Math.floor(Math.random() * 4) - 2; |
222
|
|
|
index = Math.min(Object.keys(celesta).length - 1, index); |
223
|
|
|
index = Math.max(1, index); |
224
|
|
|
if (current_notes < note_overlap) { |
225
|
|
|
current_notes++; |
226
|
|
|
if (type == 'IssuesEvent' || type == 'IssueCommentEvent') { |
227
|
|
|
clav[index].play(); |
228
|
|
|
} else if(type == 'PushEvent') { |
229
|
|
|
celesta[index].play(); |
230
|
|
|
}else{ |
231
|
|
|
playRandomSwell(); |
232
|
|
|
} |
233
|
|
|
setTimeout(function() { |
234
|
|
|
current_notes--; |
235
|
|
|
}, note_timeout); |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
// Following are the n numbers of event consumers |
240
|
|
|
// consuming n events each per second with a random delay between them |
241
|
|
|
|
242
|
|
|
function playFromQueueExchange1(){ |
243
|
|
|
var event = eventQueue.shift(); |
244
|
|
|
if(event != null && event.message != null && !shouldEventBeIgnored(event) && svg != null){ |
245
|
|
|
playSound(event.message.length*1.1, event.type); |
246
|
|
|
if(!document.hidden) |
247
|
|
|
drawEvent(event, svg); |
|
|
|
|
248
|
|
|
} |
249
|
|
|
setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000) + 500); |
250
|
|
|
$('.events-remaining-value').html(eventQueue.length); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
// This method capitalizes the string in place |
254
|
|
|
String.prototype.capitalize = function(all){ |
|
|
|
|
255
|
|
|
if(all){ |
256
|
|
|
return this.split(' ').map(function(e){ |
257
|
|
|
return e.capitalize().join(' '); |
258
|
|
|
}); |
259
|
|
|
}else{ |
260
|
|
|
return this.charAt(0).toUpperCase() + this.slice(1); |
261
|
|
|
} |
262
|
|
|
}; |
263
|
|
|
|
264
|
|
|
function drawEvent(data, svg_area) { |
265
|
|
|
var starting_opacity = 1; |
266
|
|
|
var opacity = 1 / (100 / data.message.length); |
267
|
|
|
if (opacity > 0.5) opacity = 0.5; |
|
|
|
|
268
|
|
|
|
269
|
|
|
var size = data.message.length; |
270
|
|
|
var label_text; |
271
|
|
|
var ring_radius = 80; |
272
|
|
|
var ring_anim_duration = 3000; |
273
|
|
|
svg_text_color = '#FFFFFF'; |
274
|
|
|
|
275
|
|
|
switch(data.type){ |
|
|
|
|
276
|
|
|
case "PushEvent": |
277
|
|
|
label_text = data.actorName.capitalize() + " pushed to " + data.repoName; |
278
|
|
|
edit_color = '#22B65D'; |
279
|
|
|
break; |
280
|
|
|
case "PullRequestEvent": |
281
|
|
|
label_text = data.actorName.capitalize() + " " + |
282
|
|
|
data.action + " " + " a PR for " + data.repoName; |
283
|
|
|
edit_color = '#8F19BB'; |
284
|
|
|
ring_anim_duration = 10000; |
285
|
|
|
ring_radius = 600; |
286
|
|
|
break; |
287
|
|
|
case "IssuesEvent": |
288
|
|
|
label_text = data.actorName.capitalize() + " " + |
289
|
|
|
data.action + " an issue in " + data.repoName; |
290
|
|
|
edit_color = '#ADD913'; |
291
|
|
|
break; |
292
|
|
|
case "IssueCommentEvent": |
293
|
|
|
label_text = data.actorName.capitalize() + " commented in " + data.repoName; |
294
|
|
|
edit_color = '#FF4901'; |
295
|
|
|
break; |
296
|
|
|
case "ForkEvent": |
297
|
|
|
label_text = data.actorName.capitalize() + " forked " + data.repoName; |
298
|
|
|
edit_color = '#0184FF'; |
299
|
|
|
break; |
300
|
|
|
case "CreateEvent": |
301
|
|
|
label_text = data.actorName.capitalize() + " created " + data.repoName; |
302
|
|
|
edit_color = '#00C0C0'; |
303
|
|
|
break; |
304
|
|
|
case "WatchEvent": |
305
|
|
|
label_text = data.actorName.capitalize() + " watched " + data.repoName; |
306
|
|
|
edit_color = '#E60062'; |
307
|
|
|
break; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
var no_label = false; |
|
|
|
|
311
|
|
|
var type = data.type; |
312
|
|
|
|
313
|
|
|
var abs_size = Math.abs(size); |
314
|
|
|
size = Math.max(Math.sqrt(abs_size) * scale_factor, 3); |
315
|
|
|
|
316
|
|
|
Math.seedrandom(data.message); |
317
|
|
|
var x = Math.random() * (width - size) + size; |
318
|
|
|
var y = Math.random() * (height - size) + size; |
319
|
|
|
|
320
|
|
|
var circle_group = svg_area.append('g') |
321
|
|
|
.attr('transform', 'translate(' + x + ', ' + y + ')') |
322
|
|
|
.attr('fill', edit_color) |
323
|
|
|
.style('opacity', starting_opacity); |
324
|
|
|
|
325
|
|
|
var ring = circle_group.append('circle'); |
326
|
|
|
ring.attr({r: size, stroke: 'none'}); |
327
|
|
|
ring.transition() |
328
|
|
|
.attr('r', size + ring_radius) |
329
|
|
|
.style('opacity', 0) |
330
|
|
|
.ease(Math.sqrt) |
331
|
|
|
.duration(ring_anim_duration) |
332
|
|
|
.remove(); |
333
|
|
|
|
334
|
|
|
var circle_container = circle_group.append('a'); |
335
|
|
|
circle_container.attr('xlink:href', data.eventURL); |
336
|
|
|
circle_container.attr('target', '_blank'); |
337
|
|
|
circle_container.attr('fill', svg_text_color); |
338
|
|
|
|
339
|
|
|
var circle = circle_container.append('circle'); |
340
|
|
|
circle.classed(type, true); |
341
|
|
|
circle.attr('r', size) |
342
|
|
|
.attr('fill', edit_color) |
343
|
|
|
.transition() |
344
|
|
|
.duration(max_life) |
345
|
|
|
.style('opacity', 0) |
346
|
|
|
.remove(); |
347
|
|
|
|
348
|
|
|
circle_container.on('mouseover', function() { |
349
|
|
|
circle_container.append('text') |
350
|
|
|
.text(label_text) |
|
|
|
|
351
|
|
|
.classed('label', true) |
352
|
|
|
.attr('text-anchor', 'middle') |
353
|
|
|
.attr('font-size', '0.8em') |
354
|
|
|
.transition() |
355
|
|
|
.delay(1000) |
356
|
|
|
.style('opacity', 0) |
357
|
|
|
.duration(2000) |
358
|
|
|
.each(function() { no_label = true; }) |
|
|
|
|
359
|
|
|
.remove(); |
360
|
|
|
}); |
361
|
|
|
|
362
|
|
|
var text = circle_container.append('text') |
363
|
|
|
.text(label_text) |
|
|
|
|
364
|
|
|
.classed('article-label', true) |
365
|
|
|
.attr('text-anchor', 'middle') |
366
|
|
|
.attr('font-size', '0.8em') |
367
|
|
|
.transition() |
368
|
|
|
.delay(2000) |
369
|
|
|
.style('opacity', 0) |
370
|
|
|
.duration(5000) |
371
|
|
|
.each(function() { no_label = true; }) |
|
|
|
|
372
|
|
|
.remove(); |
373
|
|
|
|
374
|
|
|
// Remove HTML of decayed events |
375
|
|
|
// Keep it less than 50 |
376
|
|
|
if($('#area svg g').length > 50){ |
377
|
|
|
$('#area svg g:lt(10)').remove(); |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
|
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.
Consider:
If you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42
will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.